package procedure;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 分支工序合并
 * @author sk.z
 */
public class MergeSubProcess {

    private static int count = 1;

    public static boolean of(Node process) {
        System.out.println("第" + (count++) + "次计算");
        Node cur = process;

        //获取最深的一个分流点
        Node forkNode = Find.deepestFork(process);

        //存在分流点
        if (Objects.nonNull(forkNode)) {
            System.out.println("分流前点:" + forkNode.show());

            //获取分流中存在的任一串流程 以List返回流程中的节点
            List<Node> subSerial = getSubSerial(forkNode);

            //存在串流程
            if (!subSerial.isEmpty()) {
                Show.subSerial(subSerial);
                //串流合并为一个节点 并计算产能 合格率
                Node newNode = Calculate.mergeSerial(subSerial);
                System.out.println("合并后:" + newNode.toString());
                //将新节点替换串流
                subSerialReplaceByNewNode(subSerial, newNode);
                return true;
            }

            //不存在串流程 开始合并并行流程
            List<Node> subParallel = getSubParallel(forkNode);
            Show.subParallel(subParallel);
            Node newNode = Calculate.mergeParallel(subParallel);
            System.out.println("合并后:" + newNode.toString());
            subParallelReplaceByNewNode(subParallel, newNode);
            return true;
        }

        return false;
    }

    //获取分流中存在的任一串流程 以List返回流程中的节点
    private static List<Node> getSubSerial(Node preStart) {
        List<Node> subSerialList = preStart.getAfterList();
        List<Node> result = new ArrayList<>();
        for (Node first : subSerialList) {
            Node next = first.getAfterList().get(0);
            if (next.getPreList().size() > 1) {  //第二节点为汇流点 此流非串联
                continue;
            }

            result.add(first);
            result.add(next);
            while (next.getAfterList().size() > 0) {
                next = next.getAfterList().get(0);
                if (next.getPreList().size() < 2) {
                    result.add(next);
                } else {
                    break;
                }
            }
            return result;
        }
        return result;
    }

    //获取分流中的并行流程 以List返回流程中节点
    private static List<Node> getSubParallel(Node preStart) {
        Map<Node, AtomicInteger> map = new HashMap<>();
        for (Node n : preStart.getAfterList()) {
            Node joinPoint = n.getAfterList().get(0);
            if (map.containsKey(joinPoint)) {
                map.get(joinPoint).incrementAndGet();
            } else {
                map.put(joinPoint, new AtomicInteger(1));
            }
        }
        Iterator<Map.Entry<Node, AtomicInteger>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Node, AtomicInteger> next = iterator.next();
            if (next.getValue().get() > 1) {
                return inJoin(preStart.getAfterList(),next.getKey().getPreList());
            }
        }
        return Collections.emptyList();
    }

    private static void subSerialReplaceByNewNode(List<Node> subSerial, Node newNode) {
        Node start = subSerial.get(0);
        Node end = subSerial.get(subSerial.size() - 1);
        Node pre = start.getPreList().get(0);
        Node after = end.getAfterList().get(0);

        for (int i = 0; i < pre.getAfterList().size(); i++) {
            Node n = pre.getAfterList().get(i);
            if (n == start) {
                pre.getAfterList().remove(i);
                pre.getAfterList().add(i, newNode);
                break;
            }
        }

        for (int i = 0; i < after.getPreList().size(); i++) {
            Node n = after.getPreList().get(i);
            if (n == end) {
                after.getPreList().remove(i);
                after.getPreList().add(i, newNode);
                break;
            }
        }

        newNode.addPre(pre);
        newNode.addAfter(after);

        for (Node n : subSerial) {
            n.setAfterList(null);
            n.setPreList(null);
        }
    }

    private static void subParallelReplaceByNewNode(List<Node> subParallel, Node newNode) {
        Node pre = subParallel.get(0).getPreList().get(0);
        Node after = subParallel.get(0).getAfterList().get(0);

        pre.getAfterList().removeIf(subParallel::contains);
        after.getPreList().removeIf(subParallel::contains);

        pre.addAfter(newNode);
        after.addPre(newNode);
        newNode.addPre(pre);
        newNode.addAfter(after);

        for (Node n : subParallel) {
            n.setAfterList(null);
            n.setPreList(null);
        }
    }

    private static List<Node> inJoin(List<Node> left, List<Node> right){
        return left.stream().filter(right::contains).collect(Collectors.toList());
    }
}
